package com.app.lzbank;

import com.alibaba.fastjson.JSON;
import com.app.lzbank.config.LZBankConfig;
import com.app.lzbank.entity.LzBankPayCallbackNotification;
import com.app.lzbank.entity.PreOrderBankRequest;
import com.app.lzbank.entity.PreOrderBankResponse;
import com.app.lzbank.entity.PreOrderBankResult;
import com.app.lzbank.utils.DateTimeBankUtils;
import com.app.lzbank.utils.SerialNoGenUtils;
import com.app.mapper.FwMapper;
import com.app.mapper.PropertyFeeLogMapper;
import com.app.mapper.TenantRelationshipMapper;
import com.app.mapper.UserMapper;
import com.app.model.*;
import com.app.service.IFwjzService;
import com.app.service.IPaymentOrderService;
import com.app.service.IZnjLogService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.binarywang.wxpay.service.WxPayService;
import com.lzbank.openbank.client2.ExternalApiClient;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

/**
 * 兰州银行聚合支付
 */
@Slf4j
@Component
public class LzBankPayService {

    @Autowired
    private LZBankConfig lzBankConfig;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private ExternalApiClient client;

    @Autowired
    private IPaymentOrderService paymentOrderService;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private WxPayService wxService;

    @Autowired
    private TenantRelationshipMapper tenantRelationshipMapper;

    @Autowired
    private IFwjzService fwjzService;

    @Autowired
    private FwMapper fwMapper;

    @Autowired
    private IZnjLogService znjLogService;

    @Autowired
    private PropertyFeeLogMapper propertyFeeLogMapper;

    @Autowired
    private LzBankPayService lzBankPayService;
    @Bean
    @Lazy
    public ExternalApiClient externalApiClient(LZBankConfig lzBankConfig) {
        System.out.println("兰州银行聚合支付初始化");
        Properties cfg = new Properties();
        cfg.setProperty("urlPrefix", lzBankConfig.getUrlPrefix());
        cfg.setProperty("appKey", lzBankConfig.getAppKey());
        cfg.setProperty("serverPublicKey",
                lzBankConfig.getServerPublicKey());
        cfg.setProperty("clientPrivateKey",
                lzBankConfig.getClientPrivateKey());
        cfg.setProperty("urlFile", lzBankConfig.getUrlFile());
        try {
            return new ExternalApiClient(cfg);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 聚合支付预下单
     */
    public PreOrderBankResult preOrder2LzBank(String orderNo, String wxOpenId, BigDecimal amount, String subject, String body, String clientIp) {
        // 创建请求对象
        PreOrderBankRequest request = new PreOrderBankRequest();
        request.setProductId(lzBankConfig.getProductId()); //商品ID
        request.setServiceId("EpayYuXiaDan"); //服务ID
        request.setTranDateTime(DateTimeBankUtils.getCurrentTime()); //交易时间
        request.setReqSn(SerialNoGenUtils.genSerialNoByDate()); //请求流水号
        request.setPartner(lzBankConfig.getPartner());//测试环境：1906281561715892461 生产环境：门户获取，同开发者认证的Merch_No
        request.setOutTradeNo(orderNo);
        request.setProductFee(amount);    //商品金额
        request.setTransportFee(BigDecimal.ZERO); //运费
        request.setTotalFee(amount); //订单总金额
        request.setBody(body); //订单说明。如果使用微信支付，微信订单详情是该字段
        request.setShowUrl(lzBankConfig.getShowUrl()); //商品展示地址
        request.setTransChannel("mb"); //交易渠道 mb-微信小程序 固定
        request.setReturnUrl(lzBankConfig.getReturnUrl()); //支付成功后跳转地址
        request.setNotifyUrl(lzBankConfig.getNotifyUrl()); //支付成功后异步通知地址
        request.setTimeStart(DateTimeBankUtils.getCurrentTime()); //订单生成时间
        request.setTimeExpire(DateTimeBankUtils.getCurrentTimePlusMinutes(5));//订单过期时间 增加5分钟
        request.setPayType("unifiedorder");//请求类型 固定上送：unifiedorder
        request.setBizType("0001");//交易类型 固定上送：0001
        request.setTerminalNo("0001"); //终端号 固定上送：0001
        request.setSign("签名值 文档暂无提供"); //签名值 文档暂无提供
        request.setTradeMode("0002"); //交易模式 固定上送：0002      -既时到账
        request.setSpbillCreateIp(clientIp);//终端IP 用户浏览器端 IP
        request.setServiceVersion("3.0"); //服务版本号
        request.setService("unifiedorder_service"); //服务名称 固定上送：unifiedorder_service
        request.setInputCharset("UTF-8"); //编码格式
        request.setFeeType("1"); //货币类型 固定上送：1    -人民币
        request.setSubject(subject); //交易物品名称或交易摘要

//        request.setSubPartner("1906281561715892461"); //二级商户号 二级商户下单上送，否则送空
//        request.setBatchNum("1"); //总笔数 二级商户下单时，订单明细中子订单的笔数,否则送1
//        request.setTradeDetails("测试订单明细"); //交易明细 二级商户下单该值为必填，详见二级商户下单说明

        request.setAppSpaceId(lzBankConfig.getAppSpaceId()); //收银台域ID 如果是小程序支付，请上送小程序 appid
        request.setAppUserId(wxOpenId); //用户标识 openId
        request.setAppType("mp"); //APP类型(h5:h5支付/mp:微信小程序支付/zfb_mp:支付宝小程序支付/mer_wx_mp:微信小程序jsapi支付/mer_zfb_mp:支付宝小程序jsapi支付)
        request.setBatchNum(1);
        return preOrder(request);

    }
    public PreOrderBankResult preOrder(PreOrderBankRequest preOrderBankRequest) {
        try {
            log.info("兰州银行聚合支付预下单请求:{}", JSON.toJSONString(preOrderBankRequest));
            byte[] resBytes = client.execute(JSON.toJSONString(preOrderBankRequest).getBytes("UTF-8"));
            String responeJsonStr = new String(resBytes, "UTF-8");
            log.info("兰州银行聚合支付预下单响应:{}", responeJsonStr);
            PreOrderBankResponse bankResponse = JSON.parseObject(responeJsonStr, PreOrderBankResponse.class);
//            log.info("兰州银行聚合支付预下单响应2:{}", bankResponse);
            if (!bankResponse.getSysRespCode().equals("000000")) {
                throw new RuntimeException(bankResponse.getSysRespDesc());
            }
            return new PreOrderBankResult(preOrderBankRequest.getOutTradeNo(), preOrderBankRequest.getTotalFee(), bankResponse.getPayUrl());
        } catch (Exception e) {
            throw new RuntimeException("调用银行预下单失败：" + e.getMessage());
        }
    }
    //支付回调
    public String payCallBack2LzBank(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //使用NetServer解密
        //获取request中的加密数据
        // 获取请求体的字节数组
        byte[] data = IOUtils.toByteArray(request.getInputStream());
        try {
            byte[] decryptBytes = client.decrypt(data);
            String decryptStr = new String(decryptBytes, "UTF-8");
            log.info("兰州银行聚合支付回调响应:{}", decryptStr);
            LzBankPayCallbackNotification payCallback = JSON.parseObject(decryptStr, LzBankPayCallbackNotification.class);
            // 设置分布式锁 去掉重复订单
            Boolean locked = redisTemplate.opsForValue().setIfAbsent("LZBANK_PAYCALLBACK_NOTIFICATION_" + payCallback.getOutTradeNo(), "locked", 30, TimeUnit.MINUTES);
            if (!locked) {
                log.info("重复请求，订单号={}", payCallback.getOutTradeNo());
                return "";
            }
            payCallBackHandler(payCallback.getOutTradeNo());
            //处理业务逻辑
            //去重处理
            //更新订单状态
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return "";
    }

    private void payCallBackHandler(String outTradeNo) {
        QueryWrapper<PaymentOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("order_id", outTradeNo);
        PaymentOrder paymentOrder = paymentOrderService.getOne(queryWrapper);
        if(paymentOrder != null && paymentOrder.getPayStatus() == 0){

            Fw fw = fwMapper.getFwInfoByIdValue(paymentOrder.getIdValue());

//            QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
//            userQueryWrapper.eq("id", paymentOrder.getUserId());
//            User user = userMapper.selectOne(userQueryWrapper);

            //更新订单状态
            paymentOrder.setPayTime(LocalDateTime.now());
            paymentOrder.setPayStatus(1);
            paymentOrderService.updateById(paymentOrder);

            //保证金
            if(paymentOrder.getDepositPaid() != null && paymentOrder.getDepositPaid().compareTo(BigDecimal.ZERO) > 0){
                if (paymentOrder.getIdValue() != null){
                    QueryWrapper<TenantRelationship> queryWrapper1 = new QueryWrapper<>();
                    queryWrapper1.eq("unique_id", paymentOrder.getIdValue());
                    queryWrapper1.orderByDesc("create_time");
                    queryWrapper1.last("limit 1");
                    TenantRelationship tenantRelationship = tenantRelationshipMapper.selectOne(queryWrapper1);
                    tenantRelationship.setNewDepositPaymentTime(LocalDateTime.now());
                    tenantRelationship.setNewDeposit(paymentOrder.getDepositPaid());
                    tenantRelationshipMapper.updateById(tenantRelationship);
                }
            }
            //房租
            if (paymentOrder.getRent() != null && paymentOrder.getRent().compareTo(BigDecimal.ZERO) > 0){

                QueryWrapper<Fwjz> fwjzQueryWrapper = new QueryWrapper<>();
                fwjzQueryWrapper.eq("unique_id", paymentOrder.getIdValue());
                fwjzQueryWrapper.orderByDesc("actual_time");
                fwjzQueryWrapper.last("limit 1");
                Fwjz fwjz = fwjzService.getOne(fwjzQueryWrapper);

                Fwjz newFwjz = new Fwjz();
                if (fwjz != null){
                    //最后的缴纳至时间（此处最后的至缴纳时间，当月是没有交的，所以取上月最后一次缴纳的）
                    LocalDateTime untilPayTime = fwjz.getUntilPayTime();

                    //加上订单里记录的缴纳了几个月，得出最新的缴纳至时间
                    int month = paymentOrder.getRentMonth();
                    LocalDateTime newUntilPayTime = untilPayTime.plusMonths(month);

                    newFwjz.setPayTime(untilPayTime);
                    newFwjz.setUntilPayTime(newUntilPayTime);

                }else {//没有历史缴费记录
                    //起始缴纳时间
                    LocalDateTime liveTime =fw.getLatestLeaseRelationshipTime();
                    //加上缴纳月份 算出缴纳至时间
                    int month = paymentOrder.getRentMonth();
                    LocalDateTime newUntilPayTime = liveTime.plusMonths(month);

                    newFwjz.setPayTime(liveTime);
                    newFwjz.setUntilPayTime(newUntilPayTime);
                }

                newFwjz.setOrderId(paymentOrder.getOrderId());
                newFwjz.setUniqueId(paymentOrder.getIdValue());
                newFwjz.setIdentityNumber(fwjz.getIdentityNumber());
                newFwjz.setBuildRentTime(fwjz.getBuildRentTime());
                newFwjz.setActualTime(LocalDateTime.now());
                newFwjz.setAmount(paymentOrder.getRent().doubleValue());
                newFwjz.setActuallyPaidAmount(paymentOrder.getRent().doubleValue());
                if (paymentOrder.getType()==1){
                    newFwjz.setOwedAmount(paymentOrder.getRent().doubleValue());
                    newFwjz.setLateDays(paymentOrder.getRentLateDays());
                }
//                //生成收据
//                String receiptNumber = ReceiptGenerator.generateReceipt(user.getRealName(),fwjz.getLeaseCertificateNumber(),newFwjz.getPayTime(),
//                        newFwjz.getUntilPayTime(),null,null,paymentOrder.getRent(),null,fw.getUsableArea(),
//                        new BigDecimal(fw.getUnitPricePerSquare()),null,new BigDecimal(fw.getCalculatedPrice()),fw.getAddressValue());

//                if (receiptNumber != null){
//                    newFwjz.setReceiptNumber(receiptNumber);
//                    newFwjz.setPrintFlag(1);
//                }
                newFwjz.setLeaseCertificateNumber(fw.getLeaseCertificateNumber());
                newFwjz.setOrderNumber(paymentOrder.getOrderId());
                fwjzService.save(newFwjz);

            }
            //滞纳金
            if (paymentOrder.getLateFee() != null && paymentOrder.getLateFee().compareTo(BigDecimal.ZERO) > 0){

                //拿出滞纳天数
                Integer lateDays = paymentOrder.getLateDays();
                //上个月的最后一天
                LocalDateTime lastDayOfMonth = LocalDateTime.now().with(java.time.temporal.TemporalAdjusters.lastDayOfMonth());
                //lastDayOfMonth往前推lateDays，得出开始计算滞纳金的那天
                LocalDateTime startCalcLateFeeTime = lastDayOfMonth.minusDays(lateDays);

                ZnjLog znjLog = new ZnjLog();
                znjLog.setIdValue(fw.getIdValue());
                znjLog.setMphValue(fw.getMphValue());
                znjLog.setUserName(fw.getCurrentTenantName());
                znjLog.setUserIdCard(fw.getCurrentTenantIdCard());
                znjLog.setContactInfo(fw.getWechatBoundPhone());
                znjLog.setZnksValue(startCalcLateFeeTime);
                znjLog.setZnzzValue(lastDayOfMonth);
                znjLog.setZnjValue(paymentOrder.getLateFee());
                znjLog.setZntsValue(lateDays);
                znjLog.setOrderNumber(paymentOrder.getOrderId());
                znjLogService.save(znjLog);

            }
            //物业费
            if (paymentOrder.getPropertyFee() != null && paymentOrder.getPropertyFee().compareTo(BigDecimal.ZERO) > 0){

                QueryWrapper<PropertyFeeLog> propertyFeeLogQueryWrapper = new QueryWrapper<>();
                propertyFeeLogQueryWrapper.eq("id_value",fw.getIdValue());
                propertyFeeLogQueryWrapper.orderByDesc("payment_end");
                propertyFeeLogQueryWrapper.last("limit 1");
                PropertyFeeLog propertyFeeLog = propertyFeeLogMapper.selectOne(propertyFeeLogQueryWrapper);
                if (propertyFeeLog != null){

                    //最后的缴纳至时间（此处最后的至缴纳时间，当月是没有交的，所以取上月最后一次缴纳的）
                    LocalDateTime untilPayTime = propertyFeeLog.getPaymentEnd();

                    //加上订单里记录的缴纳了几个月，得出最新的缴纳至时间
                    int month = paymentOrder.getPropertyFeeMonth();
                    LocalDateTime newUntilPayTime = untilPayTime.plusMonths(month);

                    PropertyFeeLog newPropertyFeeLog = new PropertyFeeLog();
                    newPropertyFeeLog.setIdValue(fw.getIdValue());
                    newPropertyFeeLog.setMphValue(fw.getMphValue());
                    newPropertyFeeLog.setUserName(fw.getCurrentTenantName());
                    newPropertyFeeLog.setUserIdCard(fw.getCurrentTenantIdCard());
                    newPropertyFeeLog.setContactInfo(fw.getWechatBoundPhone());
                    newPropertyFeeLog.setPaymentStart(untilPayTime);
                    newPropertyFeeLog.setPaymentEnd(newUntilPayTime);
                    newPropertyFeeLog.setPaymentFee(paymentOrder.getPropertyFee());
                    newPropertyFeeLog.setPaymentTime(LocalDateTime.now());
                    newPropertyFeeLog.setPaymentStatus("1");
                    newPropertyFeeLog.setUserId(paymentOrder.getUserId());
                    newPropertyFeeLog.setOrderNumber(paymentOrder.getOrderId());
                    propertyFeeLogMapper.insert(newPropertyFeeLog);

                }else {
                    LocalDateTime liveTime =fw.getLatestLeaseRelationshipTime();
                    LocalDateTime nextMonthFirstDay = LocalDateTime.now();

                    PropertyFeeLog newPropertyFeeLog = new PropertyFeeLog();
                    newPropertyFeeLog.setIdValue(fw.getIdValue());
                    newPropertyFeeLog.setMphValue(fw.getMphValue());
                    newPropertyFeeLog.setUserName(fw.getCurrentTenantName());
                    newPropertyFeeLog.setUserIdCard(fw.getCurrentTenantIdCard());
                    newPropertyFeeLog.setContactInfo(fw.getWechatBoundPhone());
                    newPropertyFeeLog.setPaymentStart(liveTime);
                    newPropertyFeeLog.setPaymentEnd(nextMonthFirstDay);
                    newPropertyFeeLog.setPaymentFee(paymentOrder.getPropertyFee());
                    newPropertyFeeLog.setPaymentTime(LocalDateTime.now());
                    newPropertyFeeLog.setPaymentStatus("1");
                    newPropertyFeeLog.setUserId(paymentOrder.getUserId());
                    newPropertyFeeLog.setOrderNumber(paymentOrder.getOrderId());
                    propertyFeeLogMapper.insert(newPropertyFeeLog);
                }
            }
        }
    }
}
